package classfile

import (
	"fmt"
	. "jvm/typedef"
	"math"
)

type ConstantInfo interface {
	readInfo(reader *BytecodeReader)
	Print()
}

// public
func newConstantInfo(reader *BytecodeReader, constantPool ConstantPool) ConstantInfo {
	tag := reader.readU1()
	switch tag {
	case CONSTANT_Integer:
		return &ConstantInteger{}
	case CONSTANT_Float:
		return &ConstantFloat{}
	case CONSTANT_Long:
		return &ConstantLong{}
	case CONSTANT_Double:
		return &ConstantDouble{}
	case CONSTANT_Utf8:
		return &ConstantUtf8{}
	case CONSTANT_String:
		return &ConstantString{
			constantPool: constantPool,
		}
	case CONSTANT_Class:
		return &ConstantClass{
			constantPool: constantPool,
		}
	case CONSTANT_Fieldref:
		ref := ConstantFieldref{}
		ref.constantPool = constantPool
		return &ref
	case CONSTANT_Methodref:
		ref := ConstantMethodref{}
		ref.constantPool = constantPool
		return &ref
	case CONSTANT_InterfaceMethodref:
		ref := ConstantInterfaceMethodref{}
		ref.constantPool = constantPool
		return &ref
	case CONSTANT_NameAndType:
		return &ConstantNameAndType{
			constantPool: constantPool,
		}
	default:
		panic("字节码格式错误: constant pool tag!")
	}
}

//***********************************************************
//private

//***********************************************************
//getter

// ***********************************************************

// const
const (
	CONSTANT_Class              = 7
	CONSTANT_Fieldref           = 9
	CONSTANT_Methodref          = 10
	CONSTANT_InterfaceMethodref = 11
	CONSTANT_String             = 8
	CONSTANT_Integer            = 3
	CONSTANT_Float              = 4
	CONSTANT_Long               = 5
	CONSTANT_Double             = 6
	CONSTANT_NameAndType        = 12
	CONSTANT_Utf8               = 1
	// CONSTANT_MethodHandle       = 15
	// CONSTANT_MethodType         = 16
	// CONSTANT_InvokeDynamic      = 18
)

// ***********************************************************
type ConstantInteger struct {
	val I4
}

func (self *ConstantInteger) readInfo(reader *BytecodeReader) {
	self.val = I4(reader.readU4())
}
func (self *ConstantInteger) Print() {
	fmt.Println("Integer\t\t", self.val)
}
func (self *ConstantInteger) Val() I4 {
	return self.val
}

// ***********************************************************
type ConstantLong struct {
	val I8
}

func (self *ConstantLong) Val() I8 {
	return self.val
}
func (self *ConstantLong) readInfo(reader *BytecodeReader) {
	self.val = I8(reader.readU8())
}
func (self *ConstantLong) Print() {
	fmt.Println("Long\t\t", self.val)
}

// ***********************************************************
type ConstantFloat struct {
	val F4
}

func (self *ConstantFloat) Val() F4 {
	return self.val
}
func (self *ConstantFloat) readInfo(reader *BytecodeReader) {
	self.val = F4(math.Float32frombits(uint32(reader.readU4())))
}
func (self *ConstantFloat) Print() {
	fmt.Println("Float\t\t", self.val)
}

// ***********************************************************
type ConstantDouble struct {
	val F8
}

func (self *ConstantDouble) Val() F8 {
	return self.val
}
func (self *ConstantDouble) readInfo(reader *BytecodeReader) {
	self.val = F8(math.Float64frombits(uint64(reader.readU8())))
}
func (self *ConstantDouble) Print() {
	fmt.Println("Double\t\t", self.val)
}

// ***********************************************************
type ConstantUtf8 struct {
	val string
}

func (self *ConstantUtf8) Val() string {
	return self.val
}
func (self *ConstantUtf8) readInfo(reader *BytecodeReader) {
	length := reader.readU2()
	data := reader.readBytes(uint32(length))
	self.val = string(data)
}
func (self *ConstantUtf8) Print() {
	fmt.Println("Utf8\t\t", self.val)
}

// ***********************************************************
type ConstantString struct {
	constantPool ConstantPool
	stringIndex  U2
}

func (self *ConstantString) String() string {
	return self.constantPool.getUtf8(self.stringIndex)
}
func (self *ConstantString) readInfo(reader *BytecodeReader) {
	self.stringIndex = reader.readU2()
}
func (self *ConstantString) Print() {
	fmt.Printf("String\t\t#%d\t\t%s\n", self.stringIndex, self.constantPool.getUtf8(self.stringIndex))
}

// ***********************************************************
type ConstantClass struct {
	constantPool ConstantPool
	nameIndex    U2
}

func (self *ConstantClass) readInfo(reader *BytecodeReader) {
	self.nameIndex = reader.readU2()
}

// public
func (self *ConstantClass) Print() {
	fmt.Printf("Class\t\t#%d\t\t%s\n", self.nameIndex, self.constantPool.getUtf8(self.nameIndex))
}
func (self *ConstantClass) ClassName() string {
	return self.constantPool.getUtf8(self.nameIndex)
}

// ***********************************************************
type ConstantNameAndType struct {
	constantPool    ConstantPool
	nameIndex       U2
	descriptorIndex U2
}

func (self *ConstantNameAndType) readInfo(reader *BytecodeReader) {
	self.nameIndex = reader.readU2()
	self.descriptorIndex = reader.readU2()
}
func (self *ConstantNameAndType) Print() {
	fmt.Printf("NameAndType\t\t#%d:%d\t\t%s:%s\n", self.nameIndex, self.descriptorIndex,
		self.constantPool.getUtf8(self.nameIndex), self.constantPool.getUtf8(self.descriptorIndex))
}
func (self *ConstantNameAndType) Name() string {
	return self.constantPool.getUtf8(self.nameIndex)
}

func (self *ConstantNameAndType) Descriptor() string {
	return self.constantPool.getUtf8(self.descriptorIndex)
}

// ***********************************************************
type ConstantFieldref struct {
	ConstantRef
}

func (self *ConstantFieldref) Print() {
	nameAndType := self.constantPool.getConstantInfo(self.nameAndTypeIndex).(*ConstantNameAndType)
	refType := "Fieldref"
	class := self.constantPool.getConstantInfo(self.classIndex).(*ConstantClass)
	fmt.Printf("%s\t\t#%d:%d\t\t%s.%s:%s\n", refType, self.classIndex, self.nameAndTypeIndex,
		self.constantPool.getUtf8(class.nameIndex), self.constantPool.getUtf8(nameAndType.nameIndex), self.constantPool.getUtf8(nameAndType.descriptorIndex))
}

// ***********************************************************
type ConstantMethodref struct{ ConstantRef }

func (self *ConstantMethodref) Print() {
	nameAndType := self.constantPool.getConstantInfo(self.nameAndTypeIndex).(*ConstantNameAndType)
	refType := "Methodref"
	class := self.constantPool.getConstantInfo(self.classIndex).(*ConstantClass)
	fmt.Printf("%s\t\t#%d:%d\t\t%s.%s:%s\n", refType, self.classIndex, self.nameAndTypeIndex,
		self.constantPool.getUtf8(class.nameIndex), self.constantPool.getUtf8(nameAndType.nameIndex), self.constantPool.getUtf8(nameAndType.descriptorIndex))
}

// ***********************************************************
type ConstantInterfaceMethodref struct{ ConstantRef }

func (self *ConstantInterfaceMethodref) Print() {
	nameAndType := self.constantPool.getConstantInfo(self.nameAndTypeIndex).(*ConstantNameAndType)
	refType := "InterfaceMethodref"
	class := self.constantPool.getConstantInfo(self.classIndex).(*ConstantClass)
	fmt.Printf("%s\t\t#%d:%d\t\t%s.%s:%s\n", refType, self.classIndex, self.nameAndTypeIndex,
		self.constantPool.getUtf8(class.nameIndex), self.constantPool.getUtf8(nameAndType.nameIndex), self.constantPool.getUtf8(nameAndType.descriptorIndex))
}

// ***********************************************************
type ConstantRef struct {
	constantPool     ConstantPool
	classIndex       U2
	nameAndTypeIndex U2
}

func (self *ConstantRef) readInfo(reader *BytecodeReader) {
	self.classIndex = reader.readU2()
	self.nameAndTypeIndex = reader.readU2()
}
func (self *ConstantRef) GetConstantClass() *ConstantClass {
	return self.constantPool.getConstantClass(self.classIndex)
}
func (self *ConstantRef) GetNameAndType() *ConstantNameAndType {
	nameAndType := self.constantPool.getConstantInfo(self.nameAndTypeIndex).(*ConstantNameAndType)
	return nameAndType
}

//***********************************************************
